package cn.matezk.iot;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import org.apache.log4j.Logger;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class App {
    private static Logger logger = Logger.getLogger(App.class);
    private static Printer printer = new Printer();

    private static final String SDK_VERSION = "12020126";
    private static final String PRODUCT_ID = "Y7JQV8K83D";
    private static final String PRODUCT_KEY = "gN4ARH5JIxNf7mgPrE2SwBqf";
    private static String deviceName = "CDL";

    // 测试的
    private static final List<String> testProducts = new ArrayList<>();
    // 大标签
    private static final List<String> bigLabels = new ArrayList<>();

    static {
        File file = new File("params.json");
        if (file.exists()) {
            try {
                FileInputStream fin = new FileInputStream(file);
                byte bs[] = new byte[fin.available()];
                fin.read(bs);
                JSONObject params = JSONUtil.parseObj(new String(bs));
                if (params.containsKey("deviceName")) {
                    deviceName = params.getStr("deviceName");
                }
                if (params.containsKey("pTest")) {
                    JSONArray products = params.getJSONArray("pTest");
                    for (int i = 0; i < products.size(); i ++) {
                        testProducts.add(products.getStr(i));
                    }
                }
                if (params.containsKey("bLabel")) {
                    JSONArray products = params.getJSONArray("bLabel");
                    for (int i = 0; i < products.size(); i ++) {
                        bigLabels.add(products.getStr(i));
                    }
                }
                fin.close();
            } catch (IOException e) {
                logger.error(e);
            }
        }
    }

    private static void uploadPrintTimes(MqttClient client, int times) {
        JSONObject property = new JSONObject();
        property.set("count", times);
        JSONObject reportObj = new JSONObject();
        reportObj.set("method", "report");
        reportObj.set("clientToken", IdUtil.fastSimpleUUID());
        reportObj.set("timestamp", System.currentTimeMillis());
        reportObj.set("params", property);
        MqttMessage msg = new MqttMessage();
        msg.setPayload(reportObj.toString().getBytes(StandardCharsets.UTF_8));
        msg.setQos(0);
        msg.setRetained(false);
        try {
            client.publish(String.format("$thing/up/property/%s/%s", PRODUCT_ID, deviceName), msg);
        } catch (MqttException e) {
            logger.error(e);
        }
    }

    public static void execute(MqttClient client, PrintParams params) throws TencentCloudSDKException, SQLException {
        if ("100001".equals(params.getProductId())) {
            String content = String.format("IMEI:%s,SN:%s", params.getDeviceName().split("[,;]")[0], params.getDeviceName().split("[,;]")[1]);
            boolean success = printer.printQr(content, null, 50, 27, TscLibDll.CellWidth.W2);
            if (!success) {
                logger.error("打印机调用失败");
                return;
            } else {
                int count = SqliteUtils.addOne(params.getProductId(), params.getDeviceName().split("[,;]")[0]);
                uploadPrintTimes(client, count);
            }
            return;
        } else if ("IMEI".equals(params.getProductId())) {
            boolean success = printer.printQr(params.getDeviceName(), params.getDeviceName().substring(params.getDeviceName().length() - 4), 43, 4, TscLibDll.CellWidth.W3);
            if (!success) {
                logger.error("打印机调用失败");
                return;
            } else {
                int count = SqliteUtils.addOne(params.getProductId(), params.getDeviceName());
                uploadPrintTimes(client, count);
            }
            return;
        }
        if (!NumberUtil.isNumber(params.getProductId())) {
            // 获取设备最后上报的ICCID
            String iccid = TencentUtils.getDeviceIccid(params.getProductId(), params.getDeviceName());
            params.setIccid(iccid);
        }
        // 调用接口
        logger.info("调用远程接口开始");
        HttpResponse response = new HttpRequest("http://lianlian.matezk.cn/openApi/exFactory")
                .method(Method.POST).body(JSONUtil.toJsonStr(params)).execute();
        logger.info("调用远程接口结束");
        try {
            if (response.isOk()) {
                String string = JSONUtil.createObj().set("ProductId", params.getProductId()).set("DeviceName", params.getDeviceName()).toString();
                boolean success = printer.printQr(string, params.getDeviceName().substring(params.getDeviceName().length() - 4));
                if (!success) {
                    logger.error("打印机调用失败");
                } else {
                    int count = SqliteUtils.addOne(params.getProductId(), params.getDeviceName());
                    uploadPrintTimes(client, count);
                }
            } else {
                logger.error(response.body());
            }
        } finally {
            response.close();
        }
        if (testProducts.contains(params.getProductId())) {
            new Thread(() -> {
                logger.info(String.format("设备%s/%s执行测试任务开始", params.getProductId(), params.getDeviceName()));
                JSONObject object = JSONUtil.createObj();
                for (int i = 0; i < 20; i ++) {
                    object.set("switch_1", 1 - (i % 2));
                    TencentUtils.controlDevice(params.getProductId(), params.getDeviceName(), object);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {}
                }
                logger.info(String.format("设备%s/%s测试任务执行完毕", params.getProductId(), params.getDeviceName()));
            }).start();
        }
    }

    public static void main(String[] args) throws MqttException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, SQLException {
        MqttClient client = new MqttClient(String.format("tcp://%s.iotcloud.tencentdevices.com:1883", PRODUCT_ID), PRODUCT_ID.concat(deviceName), new MemoryPersistence());
        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(true);
        //设置连接用户名
        options.setUserName(PRODUCT_ID.concat(deviceName).concat(";").concat(SDK_VERSION).concat(";").concat(getConnectId()).concat(";").concat(String.valueOf(System.currentTimeMillis() / 1000 + 60 * 60 * 24 * 365)));
        //设置连接密码
        String psk = TencentUtils.dynamicRegister(PRODUCT_ID, PRODUCT_KEY, deviceName);
        String password = HmacSha256.getSignature(options.getUserName().getBytes(), Base64.decode(psk, Base64.DEFAULT)) + ";hmacsha256";
        options.setPassword(password.toCharArray());
        //设置超时时间，单位为秒
        options.setConnectionTimeout(100);
        //设置心跳时间 单位为秒，表示服务器每隔 1.5*20秒的时间向客户端发送心跳判断客户端是否在线
        options.setKeepAliveInterval(60);

        //设置回调
        client.setCallback(new MqttCallback() {
            @Override
            public void connectionLost(Throwable throwable) {
                try {
                    client.reconnect();
                } catch (MqttException e) {
                    logger.error(e);
                }
                logger.warn(String.format("客户端%s离线", deviceName));
            }

            @Override
            public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
                String items[] = topic.split("/");
                if ("action".equals(items[2])) {
                    new Thread(() -> {
                        JSONObject payload = JSONUtil.parseObj(new String(mqttMessage.getPayload()));
                        JSONObject input = payload.getJSONObject("params");
                        PrintParams params = JSONUtil.toBean(input, PrintParams.class);
                        logger.info(String.format("收到打印任务 产品ID: %s 设备名称: %s 流量套餐: %d年 余额套餐: %d元 appKey: %s",
                                params.getProductId(), params.getDeviceName(), params.getFlowPkg(), params.getAmount() / 100, ObjectUtil.isEmpty(params.getAppKey()) ? "无" : params.getAppKey()));
                        // 回复
                        String clientToken = payload.getStr("clientToken");
                        JSONObject reply = JSONUtil.createObj();
                        reply.set("method", "action_reply");
                        reply.set("clientToken", clientToken);
                        reply.set("code", 0);
                        reply.set("status", "success");
                        MqttMessage rmsg = new MqttMessage();
                        rmsg.setPayload(reply.toString().getBytes(StandardCharsets.UTF_8));
                        rmsg.setQos(0);
                        rmsg.setRetained(false);
                        try {
                            client.publish(String.format("$thing/up/action/%s/%s", PRODUCT_ID, deviceName), rmsg);
                            // 执行打印
                            execute(client, params);
                        } catch (MqttException | TencentCloudSDKException e) {
                            logger.error(e);
                        } catch (SQLException e) {
                            logger.error(e);
                        }
                    }).start();
                }
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {

            }
        });
        client.connect(options);
        if (client.isConnected()) {
            logger.info(String.format("客户端%s上线", deviceName));
            int count = SqliteUtils.count();
            uploadPrintTimes(client, count);
            client.subscribe(String.format("$thing/down/property/%s/%s", PRODUCT_ID, deviceName));
            client.subscribe(String.format("$thing/down/action/%s/%s", PRODUCT_ID, deviceName));
        }
    }

    protected static String getConnectId() {
        StringBuffer connectId = new StringBuffer();
        for (int i = 0; i < 5; i++) {
            int flag = (int) (Math.random() * Integer.MAX_VALUE) % 3;
            int randNum = (int) (Math.random() * Integer.MAX_VALUE);
            switch (flag) {
                case 0:
                    connectId.append((char) (randNum % 26 + 'a'));
                    break;
                case 1:
                    connectId.append((char) (randNum % 26 + 'A'));
                    break;
                case 2:
                    connectId.append((char) (randNum % 10 + '0'));
                    break;
            }
        }

        return connectId.toString();
    }
}
